(function(win){

/*
	win.JW = function(name){
		var the = $(name);
		if(the.length == 0) throw name + " can't be find.";
		var cls = the.attr("class").match(/[a-zA-Z_-]+/)[0];
		if(JW[cls] == undefined) throw ("JW." + the.attr("id") + " is undefined.");
		return JW[cls](the);
	};
*/

	if(win.coro){
		win.coro.engines["text/jwml"] = function*(script, source){
			var scripts = win.Array.from(win.document.getElementsByTagName("script"));
			if(script.hasAttribute("var")){
				win[script.getAttribute("var")] = {
					get show(){
						return this._show;
					},
					set show(val){
						win.coro.set(function*(){
							if(val == this._show) return;
							if(val){
								if(!this._tags){

									this._tags = yield win.coro(`return jsm${source}.render(new Segment(document.getElementById("${script.getAttribute("id")}")))`);


									for(var tag of this._tags) tag._display = tag.style.display;
								};

								for(var tag of this._tags){
									tag.style.display = tag._display || "block";
								};
							} else {
								for(var tag of this._tags){
									tag.style.display = "none";
								};
							}
							this._show = val;
						},this);
					}
				};
			} else {

				yield* win.coro.eval(`jsm${source}.render(new Segment(document.getElementById("${script.getAttribute("id")}")))`);


			}
		}
	} else {
		var scriptID = 0;
		var ready=()=>{
			var scripts = win.document.getElementsByTagName("script");
			for(var script of scripts){
				if(script.type == "text/jwml"){
					if(!script.hasAttribute("id")) script.setAttribute("id", "__jwml" + (scriptID++));
					if(script.hasAttribute("var")){
						win[script.getAttribute("var")] = {
							get show(){
								return this._show;
							},
							set show(val){
								if(val == this._show) return;
								if(val){
									if(!this._tags){
										this._tags = win.eval(`jsm${script.innerText}.render(new Segment(document.getElementById("${script.getAttribute("id")}")))`);
										for(var tag of this._tags) tag._display = tag.style.display;
									};

									for(var tag of this._tags){
										tag.style.display = tag._display || "block";
									};
								} else {
									for(var tag of this._tags){
										tag.style.display = "none";
									};
								}
								this._show = val;
							}
						};
					} else {
						win.eval(`jsm${script.innerText}.render(new Segment(document.getElementById("${script.getAttribute("id")}")))`)
					}
				}
			}
		};

		// The ready event handler and self cleanup method
		var completed=()=>{
			win.document.removeEventListener( "DOMContentLoaded", completed );
			win.removeEventListener( "load", completed );
			ready();
		}

		// Catch cases where $(document).ready() is called
		// after the browser event has already occurred.
		// Support: IE <=9 - 10 only
		// Older IE sometimes signals "interactive" too soon
		if ( win.document.readyState === "complete" ||
			( win.document.readyState !== "loading" && !win.document.documentElement.doScroll ) ) {

			ready();

		} else {

			// Use the handy event callback
			win.document.addEventListener( "DOMContentLoaded", completed );

			// A fallback to win.onload, that will always work
			win.addEventListener( "load", completed );
		}
	}




	/*
		模型
	*/
	class Model{
		constructor(obj){
			var model = this;
			var properties = {};
			var bindings = {};

			var proxy = new Proxy(function(expr){
				var binding = bindings[expr[0]];
				if(binding == null) binding = bindings[expr] = new Binding(properties, expr[0]);
				return binding;
			}, {
				get:function(target, property){
					if(property in properties){
						property = properties[property];
					} else {
						property = properties[property] = new Model();
					}
					return property;
				},
				set:function(target, property, value){
					var oldValue = properties[property];
					properties[property] = value;
					var newValue = properties[property];

					//如果模型的值有改变，通知值改变事件
					if(oldValue != newValue){
						target([property]).inform(newValue);
					};

					return true;
				}
			});

			if(obj){
				for(var key in obj){
					if(typeof(obj[key]) == "object"){
						proxy[key] = new Model(obj[key]);
					} else {
						proxy[key] = obj[key];
					}
				}
			};

			return proxy;
		}
	};

	/*
		绑定
	*/
	class Binding{
		constructor(properties, name){
			this._events = new Set();
			this._properties = properties;
			this._name = name;
		}
		//通知所有绑定项更改值
		inform(value, target = true){
			if(this._target) return;
			try{
				this._target = target;
				this._properties[this._name] = value;
				for(var event of this._events){
					event(value);
				};
			} finally {
				this._target = null;
			}
		}
		add(fn){
			this._events.add(fn);
		}
		//不能取名delete暂命名_del
		_del(fn){
			this._events.delete(fn);
		}
		//获取模型初始值
		get value(){
			return this._properties[this._name];
		}
	}

	/*
		默认（根）模型
		这里我们采用“_”符号，寓意为“连接”“绑定”
	*/
	//win._ = new Model();


	//jsm.js文件所在目录
	var rootPath = win.document.currentScript.src;
	rootPath = rootPath.substring(0, rootPath.lastIndexOf("/"));

	//var jsmUtils = {};

	/*
	 * 用于自动生成部件ID
	 * 当部件没有id，获取id时，会自动生成部件ID
	 */
	var genID = 100;


	//Segment

	var appendChild = function(child){


		if(child instanceof Elements){
			var ctx = {scripts:[]};

			child = child.gen(ctx);


			for(var child2 of child) win.HTMLElement.prototype.appendChild.call(this, child2);

			if(ctx.scripts.length){
				for(var script of ctx.scripts){
					try{
						script[0](script[1]);
					}catch(e){
						console.error(e);
					}
				}
			}
		} else {
			return win.HTMLElement.prototype.appendChild.apply(this, arguments);
		}
	};


	var replaceChild = function(child, source){


		if(child instanceof Elements){
			var ctx = {scripts:[]};

			child = child.gen(ctx);


			var fragment = win.document.createDocumentFragment()
			child.forEach(item=>fragment.appendChild(item));
			win.HTMLElement.prototype.replaceChild.call(this, fragment, source);

			if(ctx.scripts.length){
				for(var script of ctx.scripts){
					try{
						script[0](script[1]);
					}catch(e){
						console.error(e);
					}
				}
			}
		} else {
			return win.HTMLElement.prototype.replaceChild.apply(this, arguments);
		}
	};

	class Segment{
		constructor(){
			if(arguments.length){
				this.sources = arguments;
			} else {
				this.sources = [win.document.currentScript];
			}
		}

		appendChild(child){


			if(child instanceof win.Text){
				var span = win.document.createElement("span");

				appendChild.call(span, child);


				child = span;
			};
			
			if(this.last){
				this.last = this.last.parentNode.insertBefore(child, this.last.nextSibling);
			} else {
				var script = this.sources[0];

				replaceChild.call(script.parentNode, child, script);


				this.last = child;
				for(var i = 1; i < this.sources.length; i++){
					var argi = this.sources[i];
					argi.parentNode.removeChild(argi);
				}
			};
		}
	};
	win.Segment = Segment;

	

	class Writer{
		constructor(node){
			var ctx = node;
			var proxy = new Proxy(function(){},{
				getPrototypeOf(target){
					return Object.getPrototypeOf(node);
				},
				apply(target, thisBinding, args){
					if(node instanceof Node){
						node = Function.prototype.call.call(node, ctx, ...args);
						ctx = node;
						return proxy;
					} else {
						return node.apply(ctx, args);
					};
				},
				get(target, property){
					var value = node[property];
					switch(property){
						case "prototype":
							return value;
						case "call":
							return Function.call;
						default:
							if(value instanceof Node){
								ctx = node;
								node = value;
								return proxy;
							} else {
								return value;
							};
					}
				},
				set(target, property, value){
					node[property] = value;
				}
			});

			return proxy;
		}
	}

	win.jsm = new Proxy(function(arg0){
		if(arguments.length){
			var elements = new Elements();
			var element = new Text(arg0);
			elements.appendChild(element);
			return new Writer(elements);
		} else return new Writer(new Elements());
	}, {
		get:function(targetFn, property){
			switch(property){
				case "widget":
					return tag;
				case "model":
					return function(obj){ return new Model(obj); };
				default:
					//if(property in jsmUtils){
					//	return jsmUtils[property];
					//} else {
						var elements = new Elements();
						var element = new Element(property);
						elements.appendChild(element);
						return new Writer(element);
					//}
					break;
			}
		}
	});

	var tag = function(){
		
	};

	var extend = function(o, o2){
		for(var key in o2){
			if(o2[key] === undefined) continue;

			if(o[key] == null){
				o[key] = o2[key];
			} else {
				var v1 = o[key], v2 = o2[key];
				switch(key){
					case "style":
						o[key] = extendObject(
							(typeof v1 == "string") ? parseStyle(v1) : v1,
							(typeof v2 == "string") ? parseStyle(v2) : v2
						);
						break;
					case "class":
						o[key] += " " + v2;
						break;
					default:
						o[key] = v2;
						break;
				};
			}
		};
		return o;
	};

	var parseStyle = function(str){
		var o = {};
		var arr = str.split(";");
		for(var item of arr){
			item = item.trim();
			if(item.length == 0) continue;

			item = item.split(":");
			if(item.length < 2) throw `SyntaxError: style=` + str;
			item[0] = item[0].trim();
			item[1] = item[1].trim();
			o[item[0]] = item[1];
		};
		return o;
	};

	var extendObject = function(o, o2){
		for(var key in o2) o[key] = o2[key];
		return o;
	};

	var toStyleString = function(o){
		var arr = [];
		for(var key in o){
			var value = o[key];
			if(value == null) continue;
			arr.push(key);
			arr.push(":");
			arr.push(value);
			arr.push(";");
		}
		return arr.join("");
	};

	class Node{

		render(target){


			if(!target){
				if(win.document.currentScript){
					target = new Segment(win.document.currentScript);
				} else {
					target = win.document.body;
				}
			}

			var ctx = {scripts:[]};

			var elements = this.gen(ctx);
			for(var element of elements) target.appendChild(element);


			if(ctx.scripts.length){
				for(var script of ctx.scripts){
					try{
						script[0](...(script[1] || []));
					}catch(e){
						console.error(e);
					}
				}
			}

			return elements;
		}
	};

	function getProxy(target){
		var proxy = new Proxy(function(){
			var parentMarkup = this, args = arguments;
			
			//如果参数数量为0，则返回父元素
			if(args.length == 0) return target.parent;

			var arg0 = args[0];
			if(!arg0) return proxy;

			switch((typeof(arg0.prototype || arg0))[0]){
				case "o": //object
					switch((typeof arg0)[0]){
						case "o": //object
							if(arg0 instanceof Binding){
								//如果是"Binding"对象，则进行模型更改通知相应节点内容
								var element = new Text(arg0.value || "");
								target.appendChild(element);
								arg0.add((val)=>{ element.textContent = val; });
							} else if(Array.isArray(arg0)){
								if(arg0 instanceof Children){
									/*
										* var inner = jsm.div.div`hello`()();
										* jsm.div(inner.children, {style:"color:red"})()
										*/

									for(var item of arg0){
										if(item instanceof Element){
											if(item.parent) item = item.clone();
											target.appendChild(item);
							
											if(args.length >= 2){
												for(var i = 1; i < args.length; i++){
													extend(item.attributes, args[i]);
												}
											}

										} else if(item instanceof Elements){
											item.children.forEach(item=>{
												if(item.parent) item = item.clone();
												target.appendChild(item);
								
												if(args.length >= 2){
													for(var i = 1; i < args.length; i++){
														extend(item.attributes, args[i]);
													}
												}
											});
												
										} else if(item instanceof Text) {
											target.appendChild(item);
										} else {
											throw `Not support:${typeof item}`;
										}
									}

								} else if(args.length >= 2){
									//jsm.div`hello ${target} world`()

									for(var i = 0; i < arg0.length; i++){
										if(i > 0){
											var argi = args[i];
											if(argi instanceof Binding){
												//如果是"Binding"对象，则进行模型更改通知相应节点内容
												var element = new Text(argi.value || "");
												target.appendChild(element);
												argi.add((val)=>{ element.textContent = val; });
											} else {
												if(argi instanceof Elements){
													target.appendChild(argi);
												} else {
													target.appendChild(new Text(argi));
												}
											}
										}
										target.appendChild(new Text(arg0[i]));
									};
								} else {
									arg0.forEach(item=>{
										switch((typeof item)[0]){
											case "s":
											case "n":
												item = new Text(item);
												break;
											default:
												if(item.parent) item = item.clone();
												break;
										};
										target.appendChild(item);
									});
								};
							} else {
								for(var i = 0; i < args.length; i++){
									extend(target.attributes, args[i]);
								}
							};
							return proxy;
						case "f": //function（script）
							target.appendChild(new Script(arg0));
							return proxy;
					};
				case "s": //string
				case "n": //number
					target.appendChild(new Text(arg0));
					return proxy;
				case "f": //function（Element, Elements）
					if(arg0 instanceof Element){
						if(arg0.parent) arg0 = arg0.clone();
						target.appendChild(arg0);
							
						if(args.length >= 2){
							for(var i = 1; i < args.length; i++){
								extend(arg0.attributes, args[i]);
							}
						}

						return proxy;
					} else if(arg0 instanceof Elements){
						arg0.children.forEach(item=>{
							if(item.parent) item = item.clone();
							target.appendChild(item);
								
							if(args.length >= 2){
								for(var i = 1; i < args.length; i++){
									extend(item.attributes, args[i]);
								}
							}
						});
						return proxy;
					} else {
						throw `Not support:${typeof arg0}`;
					}
			}
		},{
			get:function(targetFn, property){
				switch(property){

					case Symbol.toPrimitive:
						return function(hint){ console.trace(); console.error(hint); }

					case "prototype":
						return undefined;
					case "call":
						return Function.call;
					case "parent":
						return target[property];
					default:
						if(property in target){
							return target[property];
						} else {
							var element = new Element(property);
							target.appendChild.call(proxy, element);
							return element;
						}
				}
			},
			set:function(targetFn, property, value){
				target[property] = value;
				return true;
			},
			getPrototypeOf:function(){
				return Element.prototype;
			}
		});

		return proxy;
	};

	class Element extends Node{
		constructor(name){ super();
			var target = this;
			target.tagName = name;
			target.children = new Children();
			target.attributes = {};
			target.isHTMLElement = (code => { return code >= 97 && code <= 122 })(name.charCodeAt(0));
			target._tag = undefined;

			return getProxy(target);
		}

		appendChild(childElement){
			this.children.push(childElement);
			childElement.parent = this;
		}


		gen(ctx){


			if(this.isHTMLElement){
				var element = win.document.createElement(this.tagName);
				for(var attributeName in this.attributes){
					var attributeValue = this.attributes[attributeName];

					switch((typeof attributeValue)[0]){
						case "o":
							if(attributeValue instanceof Binding){
								//如果是"Binding"对象，则进行绑定更改通知
								attributeValue.add((val)=>{
									if(attributeName in element){
										element[attributeName] = val;
									} else {
										win.HTMLElement.prototype.setAttribute.call(element, attributeName, val);
									}
								});
								//如果模型有初始值，则渲染到dom中
								if(attributeValue.value != null) win.HTMLElement.prototype.setAttribute.call(element, attributeName, attributeValue.value);
								//对dom属性修改时，通知模型
								switch(this.tagName + "." + attributeName){
									case "input.value":
										$(element).change(()=>{ attributeValue.inform(element.value, element); });
										break;
									default:
										if(element.setAttribute == win.HTMLElement.prototype.setAttribute){
											this.bindings = {};
											element.setAttribute = function(name, value){
												win.HTMLElement.prototype.setAttribute.call(element, name, value);
												if(this.bindings[attributeName]) this.bindings[attributeName].inform(value, element);
											}
										};
										this.bindings[attributeName] = attributeValue;
										break;
								};
							} else {
								attributeValue = toStyleString(attributeValue);
								element.setAttribute(attributeName, attributeValue);
							}
							break;
						case "f":
							$(element).on(attributeName.replace(/^on/,""), attributeValue);
							break;
						default:
							element.setAttribute(attributeName, attributeValue);
							break;
					}
				};

				for(var i = 0; i < this.children.length; i++){

					var childElements = this.children[i].gen(ctx, element.children);


					for(var j of childElements){
						element.appendChild(j);
					};
				};

				return [element];
			} else {
				var elements = new Elements();


				this.tag()(this, new Writer(elements));
				return elements.gen(ctx);

			}
		}
		getAttribute(property){
			return this.attributes[property];
		}
		queryChildren(expression){
			for(var item of this.children){
				if(item.tagName == expression) return item;
			};
		}
		queryChildrenAll(expression){
			var elements = new Elements();
			this.children.forEach(item=>{
				if(item.tagName == expression) elements.appendChild(item);
			});
			return elements;
		}
		querySelector(expression){
			for(var item of this.children){
				if(item.tagName == expression) return item;
			};
		}
		querySelectorAll(expression){
			var elements = new Elements();
			this.children.forEach(item=>{
				if(item.tagName == expression) elements.appendChild(item);
			});
			return elements;
		}
		get root(){
			return this._root || this;
		}
		set root(value){
			this._root = value;
		}
		clone(){
			var target = new Element(this.tagName);
			target.children = this.children;
			target.attributes = this.attributes;
			target.root = this.root;
			return target;
		}

		tag(){


			if(this._tag === undefined){
				var targetTag = null;

				if(this == this.root){
					if(this.parent && !this.parent.isHTMLElement) targetTag = this.parent.tag()[this.tagName];
				} else {
					if(this.root.parent && !this.root.parent.isHTMLElement) targetTag = this.root.parent.tag()[this.tagName];
				}



				if(!targetTag){
					targetTag = tag[this.tagName];
					if(!targetTag){



	
						win.$.ajax({ url: `${rootPath}/jsm.widget/${this.tagName}.js`, async: false, dataType: "script" });
	
	

						targetTag = tag[this.tagName];
					};
				};

				this._tag = targetTag;
			};

			return this._tag;
		}
		init(){
			if(this._init) return;


			this._init = true;
		}
		get ID(){
			var attrs = this.attributes;
			if(!attrs.ID){
				attrs.ID = "pt" + (genID++);
			}

			return attrs.ID;
		}
		set ID(val){
			this.attributes.ID = val;
		}
	};

	class Elements extends Node{
		constructor(){ super();
			var target = this;
			target.children = new Children();

			return getProxy(target);
		}
		appendChild(childElement){
			this.children.push(childElement);
			childElement.parent = this;
		}

		gen(ctx){


			var arr = [];

			this.children.forEach(item=>{
				for(var j of item.gen(ctx, arr)){
					arr.push(j);
				}
			});


			return arr;
		}
	};

	class Text extends Node{
		constructor(txt=""){ super();

			if(typeof(txt) != "string"){
				console.trace();
				throw new Error("txt is not type string");
			};

			this._textContent = txt;
		}

		gen(ctx){


			if(!this.dom){
				this.dom = win.document.createTextNode(this._textContent);
			};
			return [this.dom];
		}
		clone(){
			return new Text(this.textContent);
		}
		get textContent(){
			return this._textContent;
		}
		set textContent(val){
			this._textContent = val;
			if(this.dom) this.dom.textContent = val;
		}
	};

	class Script extends Node{
		constructor(fn){ super();
			this.fn = fn;
		}

		gen(ctx, doms){


			ctx.scripts.push([this.fn, doms && Array.prototype.slice.call(doms, -this.fn.length)]);
			return [];
		}
		clone(){
			return new Script(this.fn);
		}
	};

	/*
	 * 表示Element和Elements的children属性
	 * 用来区分普通数与Children数组
	 */
	class Children extends Array{};



})(window);
